'# Copyright (c) 2008-2011 Wesley Werner
'# Source code distributed under the BSD license
Public Class MainForm

    Private WithEvents copy As ASyncCopy
    Private files As New List(Of String)
    Private targetPath As String = ""
    Private sourcePath As String = ""
    Private finalDestination As String = ""
    Private pos As Long = 0
    Private length As Long = 0


    ''' <summary>
    ''' write an entry to the log
    ''' </summary>
    ''' <param name="text"></param>
    ''' <remarks></remarks>
    Private Sub Log(ByVal text As String)

        txtLog.AppendText(String.Format("{0} - {1}{2}", Format(Date.Now, "HH:mm"), text, ControlChars.CrLf))

    End Sub

    ''' <summary>
    ''' start the copy process
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub StartCopy()

        ' setup the form
        Setupform(True, False)

        ' get the list of files to copy
        For Each item As ListViewItem In FileList.CheckedItems
            files.Add(item.Tag.ToString)
        Next

        ' start the timer to get the file positions
        progressTimer.Start()

        ' copy the next item in the queue
        CopyNextFile()

    End Sub


    ''' <summary>
    ''' start a asynchronous copy operation on the next file in the queue
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub CopyNextFile()

        ' reset the progress graph
        progressGraph.Reset()

        ' if there are no more files left to copy
        If files.Count = 0 Then

            Setupform(False, True)
            lblPosition.Text = ""
            lblLog.Text = ""
            Log(" ---< Copy Complete >---")

        Else

            ' setup the copy object
            copy = New ASyncCopy
            copy.BufferSize = My.Settings.Buffersize
            copy.ParentForm = Me

            ' get the next file to copy
            Dim nextfile As String = files(0)

            ' work out the destintion path including subfolders
            Dim relativeSource As String = nextfile.Substring(sourcePath.Length)
            finalDestination = IO.Path.Combine(targetPath, relativeSource)

            ' remove nextfile from the list of copy files
            files.Remove(nextfile)

            ' copy!
            copy.CopyFile(nextfile, finalDestination)

        End If

    End Sub


    ''' <summary>
    ''' fired when a copy starts
    ''' </summary>
    ''' <param name="filename"></param>
    ''' <param name="filesize"></param>
    ''' <remarks></remarks>
    Private Sub copy_CopyStart(ByVal filename As String, ByVal filesize As Long) Handles copy.CopyStart
        length = filesize
        progressGraph.Filesize = filesize
        Log(String.Format("copying {0}, {1}MB", IO.Path.GetFileName(filename), Format(filesize / 1000000, "F")))
    End Sub


    ''' <summary>
    ''' update the progress values
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub progressTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles progressTimer.Tick

        lblPosition.Text = String.Format("{0} MB to go", Format((length - pos) / 1000000, "#"))

    End Sub



    ''' <summary>
    ''' set the status of the listview item with the specified filename as a tag
    ''' </summary>
    ''' <param name="filename"></param>
    ''' <param name="text"></param>
    ''' <remarks></remarks>
    Private Sub SetItemStatus(ByVal filename As String, ByVal text As String)
        For Each item As ListViewItem In FileList.Items

            If item.Tag.ToString = filename Then

                ' add subitems if they're missing
                While item.SubItems.Count < FileList.Columns.Count
                    item.SubItems.Add("")
                End While

                item.SubItems(statusCol.Index).Text = text
                Exit For

            End If
        Next
    End Sub


#Region " copy events "


    ''' <summary>
    ''' fired after the Abort() method has been called
    ''' </summary>
    ''' <param name="filename"></param>
    ''' <remarks></remarks>
    Private Sub copy_CopyCancelled(ByVal filename As String) Handles copy.CopyCancelled

        Log(" ---< Copy cancelled by user >---")

        Setupform(False, True)

    End Sub


    ''' <summary>
    ''' fired after a file has been copied
    ''' </summary>
    ''' <param name="filename"></param>
    ''' <remarks></remarks>
    Private Sub copy_CopyDone(ByVal filename As String, ByVal length As Long) Handles copy.CopyDone

        Try

            lblPosition.Text = "Working..."

            If (copy.BytesFailed > 0) Then
                Log(String.Format(" - {0}MB failed to copy", copy.BytesFailed \ 1000000))
            End If

            SetItemStatus(filename, String.Format("{0}%", CInt((copy.BytesSucceeded / length) * 100)))

            ' copy the next file in the queue
            CopyNextFile()

        Catch ex As Exception
            Err(ex)
        End Try

    End Sub


    ''' <summary>
    ''' fired when something drastic went wrong and the file couldn't be copied
    ''' </summary>
    ''' <param name="filename"></param>
    ''' <param name="ex"></param>
    ''' <remarks></remarks>
    Private Sub copy_CopyError(ByVal filename As String, ByVal ex As System.Exception) Handles copy.CopyError

        SetItemStatus(filename, "Error")
        Log(String.Format(" - {0}MB failed to copy", copy.BytesFailed \ 1000000))
        CopyNextFile()

    End Sub

    ''' <summary>
    ''' grab the error'd progress graph and save it alongside the image
    ''' </summary>
    ''' <param name="filename"></param>
    ''' <remarks></remarks>
    Private Sub SaveErrordGraph(ByVal filename As String)
        Try
            Dim imgfile As String = IO.Path.Combine(IO.Path.GetDirectoryName(finalDestination), IO.Path.GetFileNameWithoutExtension(filename))
            imgfile = IO.Path.ChangeExtension(imgfile, ".png")
            progressGraph.GetGraphImage.Save(imgfile, Drawing.Imaging.ImageFormat.Png)
        Catch ex As Exception

        End Try
    End Sub


    ''' <summary>
    ''' verbose debug message from the copy class
    ''' </summary>
    ''' <param name="message"></param>
    ''' <remarks></remarks>
    Private Sub copy_CopyMessage(ByVal message As String, ByVal verbosity As Integer) Handles copy.CopyMessage

        If verbosity = 1 Then
            lblLog.Text = message
        ElseIf verbosity = 2 Then
            Log(message)
        End If

    End Sub


    ''' <summary>
    ''' fired when the copy progress changes
    ''' </summary>
    ''' <param name="position"></param>
    ''' <param name="goodRead"></param>
    ''' <param name="retryCount"></param>
    ''' <remarks></remarks>
    Private Sub copy_CopyProgress(ByVal position As Long, ByVal goodRead As Boolean, ByVal retryCount As Integer) Handles copy.CopyProgress

        ' update the progress graph if this is the first try. subsequent tries will give a false graph.
        If retryCount = 0 Then progressGraph.Setvalue(position, Not goodRead)
        pos = position

    End Sub


#End Region


    ''' <summary>
    ''' configure the form interface, which is determined by the copy state
    ''' </summary>
    ''' <param name="copying"></param>
    ''' <param name="canCopy"></param>
    ''' <remarks></remarks>
    Private Sub Setupform(ByVal copying As Boolean, ByVal canCopy As Boolean)

        CopyTool.Enabled = canCopy
        OpenDiscTool.Enabled = Not copying
        AnalyzeTool.Visible = Not copying
        AnalyzeTool.Enabled = FileList.SelectedItems.Count = 1
        AnalyzeThisFileTool.Enabled = AnalyzeTool.Enabled

        CopyTool.Visible = Not copying
        SkipTool.Visible = copying
        StopTool.Visible = copying

        ' setup other interfaces
        If copying Then
            progressTimer.Start()
        Else
            lblLog.Text = ""
            lblPosition.Text = ""
            progressTimer.Stop()
            Me.UseWaitCursor = False
        End If

    End Sub


    ''' <summary>
    ''' read the file structure of the specified path
    ''' </summary>
    ''' <param name="path"></param>
    ''' <remarks></remarks>
    Private Sub ReadStructure(ByVal path As String)

        Dim dirs() As String = IO.Directory.GetDirectories(path)

        For Each dir As String In dirs
            ReadStructure(dir)
        Next

        ' populate the file list
        Dim files() As String = IO.Directory.GetFiles(path)

        For Each file As String In files

            Dim item As New ListViewItem(IO.Path.GetFileName(file))
            item.SubItems.Add(IO.Path.GetDirectoryName(file))
            item.SubItems.Add(Filesize(file))
            item.Checked = True
            item.Tag = file
            FileList.Items.Add(item)

        Next

    End Sub


    ''' <summary>
    ''' return a string representing the filesize of the specified file
    ''' </summary>
    ''' <param name="filename"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function Filesize(ByVal filename As String) As String
        Try

            Dim fi As New IO.FileInfo(filename)
            Dim unit As String = "MB"

            ' convert to MB
            Dim d As Double = fi.Length / 1000000

            ' convert to GN
            If d > 999 Then
                d = d / 1000
                unit = "GB"
            End If

            Return String.Format("{0} {1}", FormatNumber(d, 2), unit)

        Catch ex As Exception
            Return "err"
        End Try
    End Function


    ''' <summary>
    ''' prompt for the location to copy and populate the listview
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub BrowseTool_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BrowseTool.Click

        Dim browse As New FolderBrowserDialog

        browse.Description = "NOTE: Select the root folder if you want to preserve folder structure"

        browse.RootFolder = Environment.SpecialFolder.MyComputer

        If browse.ShowDialog(Me) = Windows.Forms.DialogResult.OK Then

            SetSource(browse.SelectedPath)

        End If

    End Sub


    Private Sub SetSource(ByVal path As String)
        FileList.Items.Clear()
        sourcePath = path
        ReadStructure(path)
        FileList.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize)
        Setupform(False, True)
    End Sub


    ''' <summary>
    ''' kick off the copy operation
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub CopyTool_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CopyTool.Click

        Dim browser As New FolderBrowserDialog

        browser.ShowNewFolderButton = True

        browser.SelectedPath = targetPath

        browser.Description = "Select the destination path, the folder structure will be preserved"

        If browser.ShowDialog(Me) = Windows.Forms.DialogResult.OK Then

            targetPath = browser.SelectedPath

            ' show the log
            splitListLog.Panel2Collapsed = False

            StartCopy()

        End If

    End Sub


    ''' <summary>
    ''' stop the copy process
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub StopTool_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StopTool.Click

        Try

            ' prompt to cancel
            If MessageBox.Show("Stop copying?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2, 0) = Windows.Forms.DialogResult.Yes Then

                Me.UseWaitCursor = True

                ' clear the list of files to copy
                files.Clear()

                ' send the abort signal to the copy class
                copy.Abort()

            End If

        Catch ex As Exception
            Err(ex)
        End Try

    End Sub


    ''' <summary>
    ''' form load
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub MainForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Setupform(False, False)

        ' show settings
        txtLog.AppendText(String.Format("[Copycat initializing - Retry count = {0}, Buffer size = {1:0,0} KiB", My.Settings.RetryCount, My.Settings.Buffersize / 1000, ControlChars.CrLf))

    End Sub


    ''' <summary>
    ''' Analyze the Disc
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub AnalyzeTool_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles AnalyzeTool.Click

        If FileList.SelectedItems.Count = 1 Then

            Dim form As New AnalyzeForm(FileList.SelectedItems(0).Tag.ToString)

            form.ShowDialog(Me)

        End If

    End Sub


    ''' <summary>
    ''' file index changed
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub FileList_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles FileList.SelectedIndexChanged

        AnalyzeTool.Enabled = FileList.SelectedItems.Count = 1
        AnalyzeThisFileTool.Enabled = AnalyzeTool.Enabled

    End Sub


    Private Sub SkipTool_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SkipTool.Click
        Log(" ... cancelled by user")
        copy.AbortCurrent()
    End Sub

    Private Sub CheckAllToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckAllTool.Click
        For Each item As ListViewItem In FileList.Items
            item.Checked = True
        Next
    End Sub

    Private Sub CheckNoneToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckNoneTool.Click
        For Each item As ListViewItem In FileList.Items
            item.Checked = False
        Next
    End Sub

    Private Sub InvertChecksToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles InvertChecksTool.Click
        For Each item As ListViewItem In FileList.Items
            item.Checked = item.Checked Xor True
        Next
    End Sub

    Private Sub AnalyzeThisFileToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AnalyzeThisFileTool.Click
        AnalyzeTool.PerformClick()
    End Sub

    Private Sub AboutTool_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AboutTool.Click
        My.Forms.AboutForm.ShowDialog(Me)
    End Sub

    Private Sub OpenDiscTool_DropDownItemClicked(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles OpenDiscTool.DropDownItemClicked
        Try

            If Not IsNothing(e.ClickedItem.Tag) Then

                SetSource(CStr(e.ClickedItem.Tag))

            End If

        Catch ex As Exception
            Err(ex)
        End Try
    End Sub

    Private Sub OpenDiscTool_DropDownOpening(ByVal sender As Object, ByVal e As System.EventArgs) Handles OpenDiscTool.DropDownOpening
        Try

            ' prepare UI
            Me.UseWaitCursor = True
            Application.DoEvents()

            ' clear list
            OpenDiscTool.DropDownItems.Clear()

            ' add drives
            For Each drive As IO.DriveInfo In My.Computer.FileSystem.Drives
                If drive.DriveType = IO.DriveType.CDRom Then
                    If drive.IsReady Then
                        Dim item As New ToolStripMenuItem(String.Format("{0} [{1}]", drive.Name, drive.VolumeLabel), My.Resources.dvd)
                        item.Tag = drive.Name
                        OpenDiscTool.DropDownItems.Add(item)
                    End If
                End If
            Next

            ' seperator
            If OpenDiscTool.DropDownItems.Count > 0 Then
                OpenDiscTool.DropDownItems.Add("-")
            End If

            ' add other actions
            OpenDiscTool.DropDownItems.Add(BrowseTool)

        Catch ex As Exception
            Err(ex)
        Finally
            Me.UseWaitCursor = False
        End Try
    End Sub

    Private Sub SettingsTool_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SettingsTool.Click
        My.Forms.SettingsForm.ShowDialog(Me)
    End Sub

End Class